import { Client, ConnectConfig } from 'ssh2';

export class SSHClient {
    private conn: Client = new Client();
    private os: string | undefined;
    private cmdStdout: string = '';

    async connect(hostinfo: ConnectConfig): Promise<number> {
        const conn = this.conn;

        return new Promise((resolve: (data: number) => void, reject: (error: Error) => void) => {
            conn.on('ready', () => {
                console.log('ssh connected:', hostinfo.host + ':' + hostinfo.port);
                resolve(0);
            }).on('error', (err) => {
                console.error(err);
                reject(err);
            }).connect(hostinfo);
        });
    }

    async exec(cmd: string, silent?: boolean): Promise<string> {
        const conn = this.conn;

        this.cmdStdout = '';
        console.log('$ ' + cmd);
        return new Promise((resolve: (data: string) => void, reject: (error: Error) => void) => {
            conn.exec(cmd, (err, stream) => {
                if (err) {
                    console.error(err);
                    reject(err);
                } else {
                    stream.on('close', () => {
                        resolve(this.cmdStdout);
                    }).on('data', (data: any) => {
                        if (!silent) console.log(String(data));
                        this.cmdStdout += String(data);
                    }).stderr.on('data', (data: any) => {
                        console.error('STDERR: ' + data);
                        reject(new Error(data));
                    });
                }
            });
        });
    }

    async put(localPath: string, remotePath: string): Promise<number> {
        const conn = this.conn;

        return new Promise((resolve: (data: number) => void, reject: (error: Error) => void) => {
            conn.sftp((err, sftp) => {
                if (err) {
                    console.error(err);
                    reject(err);
                } else {
                    sftp.on('error', (err: any) => {
                        console.error(err);
                        reject(err);
                    }).fastPut(localPath, remotePath, (err: any) => {
                        if(err) {
                            console.error(err);
                            reject(err);
                        } else {
                            resolve(0);
                        }
                    });
                }
            });
        });
    }

    async get(remotePath: string, localPath: string): Promise<number> {
        const conn = this.conn;

        return new Promise((resolve: (data: number) => void, reject: (error: Error) => void) => {
            conn.sftp((err, sftp) => {
                if (err) {
                    console.error(err);
                    reject(err);
                } else {
                    sftp.on('error', (err: any) => {
                        console.error(err);
                        reject(err);
                    }).fastGet(remotePath, localPath, (err: any) => {
                        if(err) {
                            console.error(err);
                            reject(err);
                        } else {
                            console.log(`put ${localPath} finished`);
                            resolve(0);
                        }
                    });
                }
            });
        });
    }

    end(): Promise<number> {
        const conn = this.conn;

        return new Promise((resolve: (data: number) => void, reject: (error: Error) => void) => {
            conn.on('end', () => {
                console.log('ssh ended');
            }).on('close', (hadError) => {
                if(hadError) {
                    console.error('ssh closed with error');
                    resolve(1);
                } else {
                    console.log('ssh closed without error');
                    resolve(0);
                }
            }).end();
        });
    }
}